#ifndef __MQ_VIRTUALHOST_HPP__
#define __MQ_VIRTUALHOST_HPP__

// 管理交换机
#include "mq_exchange.hpp"
// 管理队列信息
#include "mq_message.hpp"
// 管理队列
#include "mq_queue.hpp"
// 绑定交换机队列信息。
#include "mq_binding.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include "../mqcommon/mq_proto.pb.h"

namespace mq
{
    class VirtualHost
    {
    public:
        using Ptr = std::shared_ptr<VirtualHost>;
        VirtualHost(const std::string& hname,
                    const std::string& basedir,
                    const std::string& dbfile)
            :_host_name(hname)
            ,_emp(std::make_shared<ExchangeManager>(dbfile))
            ,_mqmp(std::make_shared<MsgqueueManager>(dbfile))
            ,_bmp(std::make_shared<BindManager>(dbfile))
            ,_mmp(std::make_shared<MessageManager>(basedir))
        {
            MqueueMap queues = _mqmp->allQueues();
            for(auto& queue : queues)
            {
                _mmp->initQueueMessage(queue.first);
            }
            DLOG("new VirtualHost:%p",this);
        }

        bool declareExchange(
            const std::string& ename,
            ExchangeType type,
            bool durable,
            bool auto_delete,
            // std::unordered_map<std::string,std::string>& qargs
            const google::protobuf::Map<std::string, std::string>& qargs)
        {
            if(_emp->declareExchange(ename,type,durable,auto_delete,qargs) == false)
            {
                DLOG("声明交换机失败:%s",ename.c_str());

                return false;
            }
            DLOG("声明交换机成功");

            return true;
        }
        Exchange::Ptr selectExchange(const std::string& ename)
        {
            
            return _emp->selectExchange(ename);
        }
        void deleteExchange(const std::string& ename)
        {
            // 删除交换机时，需要将交换机中的绑定信息也给剔除。
            _bmp->removeExchangeBindings(ename);
            _emp->deleteExchange(ename);
            
            return ;
        }
        bool existExchange(const std::string& ename)
        {

            return _emp->exist(ename);
        }

        bool declareMsgqueue(const std::string& qname,
            bool qdurable,
            bool qexculsive,
            bool qauto_delete,
            const google::protobuf::Map<std::string,std::string>& qargs
            )
        {
            bool ret = _mqmp->declareMsgqueue(qname,qdurable,qexculsive,qauto_delete,qargs);
            if(ret == false)
            {
                DLOG("Host:Declare Queue Failed");

                return false;
            }

            _mmp->initQueueMessage(qname);

            // DLOG("Host声明队列%s成功。",qname.c_str());

            return true;
        }
        msgqueue::Ptr selectMsgQueue(const std::string& qname)
        {
            
            return _mqmp->selectMsgQueue(qname);
        }
        bool deleteQueue(const std::string& name)
        {
            _bmp->removeQueueBindings(name);
            _mmp->destroyQueueMessage(name);

            return _mqmp->deleteMsgqueue(name);
        }
        bool existMsgQueue(const std::string& qname)
        {

            return _mqmp->exist(qname);
        }

        // 指针有可能为空，绑定可能不存在。
        bool bind(const std::string& ename,
            const std::string& qname,
            const std::string& key)
        {
            Exchange::Ptr exp = _emp->selectExchange(ename);
            if(exp.get() == nullptr)
            {
                DLOG("交换机%s不存在",ename.c_str());

                return false;
            }
            msgqueue::Ptr mqp = _mqmp->selectMsgQueue(qname);
            if(mqp.get() == nullptr)
            {
                DLOG("队列%s不存在",qname.c_str());

                return false;
            }
            // 必须要交换机是持续化的，队列是可持续化的，bind绑定才可以是可持续化的。
            DLOG("绑定非持续化交换机和非持续化队列");

            return _bmp->bind(ename,qname,key,exp->durable && mqp->durable);
        }
        void unbind(const std::string& ename, const std::string& qname)
        {
            bool ret = _bmp->unbind(ename,qname);
            if(ret == false)
            {
                DLOG("解绑失败。");

                return ;
            }
            DLOG("解绑成功。");

            return ;
        }
        MsgQueueBindingMap exchangeBindings(const std::string& ename)
        {

            return _bmp->getExchangeBindings(ename);
        }
        bool existBindings(const std::string& ename,const std::string& qname)
        {

            return _bmp->exists(ename,qname);
        }

        // bp可能为空。
        bool basicPublish(const std::string& qname,BasicProperties* bp,const std::string& body)
        {
            // DLOG("Host 发布。");
            // DLOG("body:%s",body.c_str());
            msgqueue::Ptr msgp = _mqmp->selectMsgQueue(qname);
            if(msgp.get() == nullptr)
            {
                DLOG("没有找到对应的队列%s",qname.c_str());

                return false;
            }

            return _mmp->insert(qname,bp,body,msgp->durable);
        }
        // 消费消息，就是拿出消息.
        MessagePtr basicConsume(const std::string& qname)
        {
            MessagePtr msgp = _mmp->front(qname);
            if(msgp.get() == nullptr)
            {
                DLOG("该队列%s没有消息",qname.c_str());

                return MessagePtr();
            }

            return msgp;
        }
        bool basicAck(const std::string& qname, const std::string& msgid)
        {
            bool ret = _mmp->ack(qname,msgid);
            if(ret == false)
            {
                DLOG("确认队列%s消息失败：没有找到消息管理",
                        qname.c_str());
                
                return false;
            }
            return true;
        }

        MqueueMap allQueues()
        {

            return _mqmp->allQueues();
        }

        void clear()
        {
            _emp->clear();
            _mqmp->clear();
            _bmp->clear();
            _mmp->clear();
        }
    //private:
    public:
        std::string _host_name;
        mq::ExchangeManager::Ptr _emp;
        mq::MsgqueueManager::Ptr _mqmp;
        mq::BindManager::Ptr _bmp;
        mq::MessageManager::Ptr _mmp;
    };
}

#endif